/*
 *  PinpointWCS is developed by the Chandra X-ray Center
 *  Education and Public Outreach Group
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


#include <QDebug>
#include "Commands.h"


AddCommand::AddCommand(GraphicsScene *graphicsScene, const QVariant &value, CoordinateModel *model)
: QUndoCommand()
{
    // Initialize attributes
    scene = graphicsScene;
    initialPosition = value;
    dataModel = model;
}


AddCommand::~AddCommand()
{}

void AddCommand::redo()
{
    // Initialize some indices
    QModelIndex index1;
    QModelIndex index2;
    QPointF coord;
    //int row;

    // Determine the number of rows in the data model
    int numrows = dataModel->rowCount(QModelIndex());

    if (scene->reference)
    {
        // Insert a single row
        dataModel->insertRows(numrows, 1, QModelIndex());

        // Access the data
        coord = dataModel->refCoords.value(numrows);

        // Set the reference coordinate and indices
        coord = initialPosition.toPointF();
        index1 = dataModel->index(numrows, 0);
        index2 = dataModel->index(numrows, 1);
        //row = numrows;

        // Update the data
        dataModel->refCoords.replace(numrows, coord);
    }
    else
    {
        // Access the data
        coord = dataModel->epoCoords.value(numrows-1);

        // Set the epo coordinate and indices
        coord = initialPosition.toPointF();
        index1 = dataModel->index(numrows-1, 2);
        index2 = dataModel->index(numrows-1, 3);
        //row = numrows-1;

        // Update the data
        dataModel->epoCoords.replace(numrows-1, coord);
    }

    // Initialize CoordinateMarker and add to GraphicScene
    marker = new CoordinateMarker(index1, scene->centralItem);
    marker->setPos(initialPosition.toPointF());
    scene->toggleClickable(true);
    scene->clearSelection();
    scene->update();

    // Broadcast to ComputeWCS
    dataModel->compute();

    // Broadcast some info
    dataModel->emitDataChanged(index1, index2);
}


void AddCommand::undo()
{
    // Initialize some indices
    QModelIndex index1;
    QModelIndex index2;

    // Determine the number of rows in the data model
    int numrows = dataModel->rowCount(QModelIndex());

    // Remove datum from model
    if (scene->reference)
    {
        // Datum coming from FITS scene; remove an entire row
        dataModel->removeRows(numrows-1, 1, QModelIndex());
        index1 = dataModel->index(numrows-1, 0);
        index2 = dataModel->index(numrows-1, 1);
        dataModel->emitDataChanged(index1, index2);
    }
    else
    {
        // Access the data
        QPointF coord = dataModel->epoCoords.value(numrows-1);
        coord = QPointF(-1, -1);
        index1 = dataModel->index(numrows-1, 0);
        index2 = dataModel->index(numrows-1, 1);
        dataModel->epoCoords.replace(numrows-1, coord);
    }

    // Remove CoordinateMarker from GraphicsScene
    scene->removeItem(marker);
    delete marker;
    scene->toggleClickable(true);
    scene->update();

    // Broadcast to ComputeWCS
    dataModel->compute();

    // Broadcast some info
    dataModel->emitDataChanged(index1, index2);
}



MoveCommand::MoveCommand(GraphicsScene *s, const QVariant &newValue, const QVariant &oldValue, CoordinateModel *model, QModelIndex *index)
: QUndoCommand()
{
    newPos = newValue.toPointF();
    oldPos = oldValue.toPointF();
    scene = s;
    QTransform *tempTrans = new QTransform();

    // TODO: Check that this code does not introduce the bug causing
    //		 two markers to move at once.
    //marker = qgraphicsitem_cast<CoordinateMarker*>(scene->itemAt(newPos));
    marker = qgraphicsitem_cast<CoordinateMarker*>(scene->itemAt(newPos, *tempTrans));
    if (marker == 0)
    {
        QList<QGraphicsItem*> markers = scene->selectedItems();
        if (markers.isEmpty())
        {
            // Loop through the CoordinateMarkers, checking the indices
            QList<QGraphicsItem*> markers = scene->items();
            for (int i=0; i < markers.size(); i++)
            {
                marker = qgraphicsitem_cast<CoordinateMarker*>(markers.at(i));
                if (marker->index->row() == index->row())
                    break;
            }
        }
        else {
            marker = qgraphicsitem_cast<CoordinateMarker*>(scene->selectedItems()[0]);
        }
    }
    dataModel = model;
}

bool MoveCommand::mergeWith(const QUndoCommand *command)
{
    const MoveCommand *moveCommand = static_cast<const MoveCommand*>(command);
    CoordinateMarker *item = moveCommand->marker;

    if (marker != item)
        return false;

    newPos = item->scenePos();
    return true;
}

void MoveCommand::undo()
{
    // Initialize some indices
    QModelIndex index1;
    QModelIndex index2;

    // Get the row of the data
    int row = marker->index->row();

    if (scene->reference)
    {
        dataModel->refCoords.replace(row, oldPos);
        index1 = dataModel->index(row, 0);
        index2 = dataModel->index(row, 1);
    }
    else
    {
        dataModel->epoCoords.replace(row, oldPos);
        index1 = dataModel->index(row, 2);
        index2 = dataModel->index(row, 3);
    }

    // Move marker to old position
    marker->setPos(oldPos);
    marker->show();
    scene->update();

    // Broadcast to ComputeWCS
    dataModel->compute();

    // Broadcast some info
    dataModel->emitDataChanged(index1, index2);
}

void MoveCommand::redo()
{
    // Initialize some variables
    QModelIndex index1;
    QModelIndex index2;
    QTransform *tempTrans= new QTransform();
    // If the marker is destroyed by AddCommand::undo(), this code will find the marker using coordinates (shoddy...)
    //fixing old itemAt commands

    if(scene->itemAt(oldPos, *tempTrans) != 0)
    //if (scene->itemAt(oldPos) != 0)
    {
        //if (scene->itemAt(oldPos)->pos() != QPointF(0, 0))
        if(scene->itemAt(oldPos, *tempTrans)->pos() != QPointF(0,0)) {
            marker = qgraphicsitem_cast<CoordinateMarker*>(scene->itemAt(oldPos, *tempTrans));
            //marker = qgraphicsitem_cast<CoordinateMarker*>(scene->itemAt(oldPos));
        }
    } else {
        if(scene->selectedItems()[0]->pos() != QPointF(0, 0)) {
        marker = qgraphicsitem_cast<CoordinateMarker*>(scene->  selectedItems()[0]);
        }
    }
    int row = marker->index->row();

    if (scene->reference)
    {
        dataModel->refCoords.replace(row, newPos);
        index1 = dataModel->index(row, 0);
        index2 = dataModel->index(row, 1);
    }
    else
    {
        dataModel->epoCoords.replace(row, newPos);
        index1 = dataModel->index(row, 2);
        index2 = dataModel->index(row, 3);
    }

    // Move the marker to the new position
    marker->setPos(newPos);
    marker->update();
    scene->update();

    // Broadcast to ComputeWCS
    dataModel->compute();

    // Broadcast some info
    dataModel->emitDataChanged(index1, index2);
}
